﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NETMovie.Entities;
using NETMovie.Repositories;
using NETMovie.Gateways.PayPal;
using System.Configuration;
using System.Data;

namespace NETMovie.BLL
{
    public class TransactionService
    {
        public List<string> Errors
        {
            get;
            set;
        }

        public TransactionService()
        {
            Errors = new List<string>();
        }

        public Transaction GetOpenTransaction(string username)
        {
            Transaction trans = null;
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    // Check if an open transaction exists for the user and re-use it
                    trans = repo.GetOpenTransaction(username);
                    if (trans == null)
                    {
                        // Create a transaction for the user
                        trans = repo.CreateTransaction(username);
                        if (trans == null)
                        {
                            Errors.Add("Transaction could not be created");
                            return null;
                        }
                    }
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return null;
                }
            }

            return GetTransaction(trans.TransactionId);
        }

        public Transaction RentMovie(string username, int movieID)
        {
            Transaction trans = null;
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);
            
            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    // Check if an open transaction exists for the user and re-use it
                    trans = repo.GetOpenTransaction(username);

                    if (trans == null)
                    {
                        // Create a transaction for the user
                        trans = repo.CreateTransaction(username);
                        if (trans == null)
                        {
                            Errors.Add("Transaction could not be created");
                            return null;
                        }
                    }

                    bool result = repo.RentMovie(trans.TransactionId, movieID);
                    if (!result)
                    {
                        Errors.Add("Movie rental unsuccessful");
                        return null;
                    }
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return null;
                }
            }

            return trans;
        }

        public Transaction RentMovie(int transactionID, int movieID)
        {
            Transaction trans = null;
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    // Check if transaction exists
                    trans = repo.GetTransaction(transactionID);
                    if (trans == null)
                    {
                        Errors.Add("Transaction not found");
                        return null;
                    }

                    bool result = repo.RentMovie(trans.TransactionId, movieID);
                    if (!result)
                    {
                        Errors.Add("Movie rental unsuccessful");
                        return null;
                    }
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return null;
                }
            }

            return trans;
        }

        public bool UnrentMovie(int rentalID)
        {
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    bool result = repo.UnrentMovie(rentalID);
                    if (!result)
                    {
                        Errors.Add("Movie could not be removed from rental list");
                        return false;
                    }
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return false;
                }
            }

            return true;
        }

        public bool ReturnMovie(int rentalID)
        {
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    bool result = repo.ReturnMovie(rentalID);
                    if (!result)
                    {
                        Errors.Add("Movie could not be returned");
                        return false;
                    }
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return false;
                }
            }

            return true;
        }

        public Transaction GetTransaction(int transactionID)
        {
            Transaction trans = null;
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    // Check if transaction exists
                    trans = repo.GetTransaction(transactionID);
                    if (trans == null)
                    {
                        Errors.Add("Transaction not found");
                        return null;
                    }

                    // Calculate total amount
                    decimal total = 0;
                    trans.TotalAmount = 0;
                    foreach (MovieRental rental in trans.MovieRentals)
                    {
                        total += rental.Amount;
                    }

                    total += (trans.GST * total) / 100;
                    trans.TotalAmount = total;
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return null;
                }
            }

            return trans;
        }

        public List< KeyValuePair<int, string> > GetCreditCards()
        {
            PaymentGateway gateway = new PaymentGateway();
            return gateway.GetCreditCards();
        }

        public bool Checkout(Payment payment)
        {
            Transaction trans = null;
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    // Check if transaction exists
                    trans = repo.GetTransaction(payment.TransactionInfo.TransactionId);
                    if (trans == null)
                    {
                        Errors.Add("Transaction not found");
                        return false;
                    }

                    if (trans.IsPaid)
                    {
                        Errors.Add("You have already paid for these rentals");
                        return false;
                    }

                    bool result = false;
                    if (ConfigurationManager.AppSettings["BypassPayment"].Equals("0"))
                    {
                        // Make the payment
                        PaymentGateway gateway = new PaymentGateway();
                        payment.GWTransactionID = gateway.MakePayment(payment);

                        if (String.IsNullOrEmpty(payment.GWTransactionID))
                        {
                            Errors.Add("Payment is not successful");
                            return false;
                        }
                    }

                    result = repo.Checkout(payment.TransactionInfo.TransactionId, payment.TransactionInfo.TotalAmount);
                    if (!result)
                    {
                        Errors.Add("PayPal payment made, but the DB could not be updated. Contact Help Desk.");
                        return false;
                    }
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return false;
                }
            }

            return true;
        }

        public Movie GetMovieByRental(string username, int rentalID)
        {
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                try
                {
                    MovieRental rental = repo.GetRental(rentalID);
                    if (rental == null)
                    {
                        Errors.Add("Movie rental does not exist");
                        return null;
                    }

                    if (rental.ReturnDate != null)
                    {
                        Errors.Add("The movie has already been returned.");
                        return null;
                    }

                    bool authorized = false;
                    using (IUserRepository userRepo = factory.GetUserRepository())
                    {
                        User user = userRepo.GetUser(username);
                        if (user.Rentals != null)
                        {
                            foreach (MovieRental userRental in user.Rentals)
                            {
                                if (rental.MovieRentalId == userRental.MovieRentalId)
                                {
                                    authorized = true;
                                    break;
                                }
                            }
                        }
                    }

                    if (!authorized)
                    {
                        Errors.Add("You are not authorized to watch this movie.");
                        return null;
                    }

                    return rental.Movie;
                }
                catch (AbstractUserException ex)
                {
                    Errors.Add(ex.GetUserError());
                    return null;
                }
            }
        }

        public MovieRental[] GetActiveRentals(string username)
        {
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.EF);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                return repo.GetActiveRentals(username);
            }
        }

        public DataTable GetProfitableUsers(string movie, DateTime startDate, DateTime endDate)
        {
            RepositoryFactory factory = RepositoryHelper.GetFactory(RepositoryTypes.MSSQL);

            using (ITransactionRepository repo = factory.GetTransactionRepository())
            {
                return repo.GetProfitableUsers(movie, startDate, endDate);
            }
        }
    }
}
